Open
Conversation
2552158 to
e083ebc
Compare
2 tasks
Contributor
Author
|
Continued in #275 |
e083ebc to
f1ae578
Compare
d93d341 to
5a184c7
Compare
This modifies the inheritance unit test to demonstrate the sorting issue.
1e96b24 to
07662e4
Compare
Classes in generated .pyi stubs were sorted alphabetically, causing derived classes to appear before their base classes and breaking type checkers. Three changes fix this: - Parser: use module.__dict__.items() instead of inspect.getmembers() to preserve the pybind11 registration order (definition order) - Printer: replace alphabetical sort with a configurable _order_classes() dispatch supporting "definition" (default), "topological" (Kahn's algorithm ensuring bases precede derived classes), and "alphabetical" - CLI: add --sort-by option to select the class ordering strategy The topological sort ignores external bases (from other modules) and breaks ties by input position for deterministic output. Cyclic cross- references between classes (e.g. aliases, method signatures) are not inheritance cycles and are already handled by `from __future__ import annotations` in the generated stubs. Closes pybind#231 Based on the approaches in PR pybind#275 by @juelg and PR pybind#294 by @daltairwalter, informed by review feedback from @skarndev and @sizmailov. Co-Authored-By: juelg <[email protected]> Co-Authored-By: daltairwalter <[email protected]> Co-Authored-By: skarndev <[email protected]> Co-Authored-By: sizmailov <[email protected]> Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
07662e4 to
13dbbf3
Compare
This was referenced Apr 3, 2026
Open
New `--sort-by` CLI option
skarndev
reviewed
Apr 6, 2026
Co-authored-by: Skarn <[email protected]>
ax3l
commented
Apr 8, 2026
.../stubs/python-3.11/pybind11-v2.11/numpy-array-wrap-with-annotated/demo/_bindings/classes.pyi
Show resolved
Hide resolved
Extend topological class ordering in the printer to account for executable class-body dependencies, not just inheritance. In addition to base-class edges, the sorter now treats class aliases and print-safe field values that reference sibling classes as runtime dependencies, deduplicates edges, and updates cycle diagnostics to reflect the broader graph. This keeps generated .pyi files valid Python without moving declarations out of class bodies, preserving the conventional stub shape that type checkers expect. Before: ```py class ParIterBase: level: int class ParticleContainer: name: str Iterator = ParIter def process(self, arg0: ParIter) -> None: ... class ParIter(ParIterBase): def __init__(self, particle_container: ParticleContainer, level: int) -> None: ... ``` After: ```py class ParIterBase: level: int class ParIter(ParIterBase): def __init__(self, particle_container: ParticleContainer, level: int) -> None: ... class ParticleContainer: name: str Iterator = ParIter def process(self, arg0: ParIter) -> None: ... ```
Contributor
Author
|
I do not think we can easily support robust |
Remove the CLI option, we cannot robustly support it with class-body dependencies.
d363ee4 to
51dfa72
Compare
In `pybind11_stubgen/parser/mixins/parse.py`, module traversal now preserves `module.__dict__` definition order for normal pybind11 exports but also appends lazily exposed members from `dir()`/`getattr()`, so the PR keeps the ordering fix without regressing PEP 562-style module attributes. In `pybind11_stubgen/printer.py`, class dependency edges now use the first identifier component of a reference, not the last. That fixes the false-local-match issue for external names like `pkg.other.Foo`, and it also makes dotted local references like `Outer.Inner` depend on `Outer`, which is the actual runtime lookup requirement.
`handle_class()` now uses a new _iter_class_members() helper that: - preserves class_.__dict__ definition order for class-local members - then appends additional visible members from dir()/getattr() so inherited or lazily exposed names are still seen That makes nested class discovery consistent with the earlier module-level fix, so the printer's topological sort now gets real definition order as its stable tie-break for nested classes too.
185a5b8 to
acca73d
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This modifies the inheritance unit test to demonstrate the sorting issue in #231.
Classes in generated
.pyistubs were sorted alphabetically, causing derived classes to appear before their base classes and breaking type checkers. Likewise, class-body dependencies were not sorted properly, creating invalid Python stubs.Three changes fix this:
module.__dict__.items()instead ofinspect.getmembers()to preserve the pybind11 registration order (definition order)a configurable"topological" (Kahn's algorithm ensuring bases precede dependent (derived classes, class-body type references))_order_classes()dispatch supporting "definition" (default),, and "alphabetical"CLI: add--sort-byoption to select the class ordering strategyThe topological sort ignores external bases (from other modules) and breaks ties by input position for deterministic output. Cyclic cross-references between classes (e.g. aliases, method signatures) are no inheritance cycles and are already handled by
from __future__ import annotationsin the generated stubs.Fixes #231
Closes #294
Closes #275
Based on the approaches in PR #275 by @juelg and PR #294 by @daltairwalter, informed by review feedback from @skarndev and @sizmailov. Combined via Claude Code and reviewed via Codex, and manually.